#include "header.h"

typedef struct element element;
struct element
{
    int val;
    struct element *nxt;
};
typedef element* llist;

SDL_Surface *screen;

void initSDL(void);
void quadri(float,int,int);
void ortho(int,int);
void courbe(float, llist, Uint32,int,int);
void update(void);
void setPixel(int, int, Uint32);
void delspace(char*);
float f(float,llist);
float puissance(float, int);
llist add(llist, int);
llist string_to_coef(char*, llist);
void loopy(void);
void redraw(void);
void onMouseMotion(SDL_MouseMotionEvent);

float pctg = 3.01;
llist ldeg = NULL;
int x = 0;
int y = 0;
int hold = 0;

int main(int argc, char *argv[])
{
  /*sous la forme "ax^b + cx^d - ... */
  //delspace(f);
  if(argc == 2)
    {
      ldeg = string_to_coef(argv[1], ldeg);
      initSDL();
      redraw();
      SDL_SaveBMP(screen,"screen.bmp"); 
      SDL_Quit();
    }
  else if (argc > 2)
    {
      ldeg = string_to_coef(argv[1], ldeg);
      initSDL();
      redraw();
      SDL_SaveBMP(screen,"screen.bmp");
      loopy();
      SDL_Quit();
    }
  else
    {
      printf("Erreur : Pas de fonction en paramètre\n");
    }
  return EXIT_SUCCESS;
}

void redraw(void)
{
  SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 255, 255, 255));
  quadri(pctg,x,y);
  ortho(x,y);
  courbe(pctg, ldeg, SDL_MapRGB(screen->format, 0, 0, 255),x,y);
  update();
}

void initSDL(void)
{
  if (SDL_Init(SDL_INIT_VIDEO) == -1)
    {
      fprintf(stderr, "Erreur d'init SDL : %s\n", SDL_GetError());
      exit(EXIT_FAILURE);
    }
  screen = SDL_SetVideoMode(640, 640, 32, SDL_HWSURFACE);
  if (screen == NULL)
    {
      fprintf(stderr, "Erreur de chargement du mode video : %s\n", SDL_GetError());
      exit(EXIT_FAILURE);
    }
  SDL_WM_SetCaption("Aquila Graphics", NULL);
  SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 255, 255, 255));
  SDL_EnableKeyRepeat(100,100);
}

/*void initTTF(void)
{
  
}*/

void setPixel(int x, int y, Uint32 color)
{
  *((Uint32*)(screen->pixels) + x + y * screen->w) = color;
}

void update(void)
{
  SDL_UpdateRect(screen, 0, 0, 0, 0);
}

void quadri(float pctg, int xd, int yd)
{
  int x = 0;
  int y = 0;
  int midw = (screen->w /2) + xd;
  int midh = (screen->h /2) + yd;
  int ix = -(int)(midw/pctg);
  int iy = -(int)(midh/pctg);
  while (ix + (int)midw/(pctg) <= 640/pctg)
    {
      x = midw + (int)(ix*pctg);
      while (iy + (int)midh/(pctg) <= 640/pctg)
      {
	if (((iy % 10) == 0)||((ix % 10) == 0))
	  {
	    y = midh + (int)(iy*pctg);
	    setPixel( x, y, SDL_MapRGB(screen->format, 150, 150, 150));
	  }
	iy++;
      }
      ix++;
      iy = -(int)(midh/pctg);
    }
}

void ortho(int xd,int yd)
{
  int x = 0;
  int y = screen->h / 2 + yd;
  while(x < screen->w)
  {
    setPixel( x, y, SDL_MapRGB(screen->format, 0, 0, 0));	
    x++;
  }
  x = screen->w /2 + xd;
  y = 0;
  while(y < screen->h)
    {
      setPixel( x, y, SDL_MapRGB(screen->format, 0, 0, 0));
      y++;
    }
  //Fleches de bouts
  x = (screen->w /2) + xd;
  y = 0;
  int i;
  while(y <= 6)
    {
      for(i = -y/2; i <= (y/2); i++)
	{
	  setPixel( x+i , y, SDL_MapRGB(screen->format, 0, 0, 0));
	}
      y++;
    }
  x = (screen->w);
  y = (screen->h /2) + yd;
  int j = 0;
  while(j <= 6)
    {
      for(i = -j/2; i <= j/2; i++)
	{
	    setPixel( x-j , y + i, SDL_MapRGB(screen->format, 0, 0, 0));
	}
      j++;
    }
}

void courbe(float pctg, llist liste, Uint32 color,int xd,int yd)
{
  int yprec = 0;
  int x = 0;
  float xre = 0.0;
  float yre = 0.0;
  int y = 0;
  int i;
  int midw = (screen->w /2) + xd;
  int midh = (screen->h /2) + yd;
  
  while(x <= screen->w)
    {
      xre = (((float)x - (float)midw)/ 10.0)/pctg;
      yre = f(xre,liste);
      yre *= pctg;
      if((yre <= (midh/10))&&(yre >= (midh/10) - 64))
	{
	  y = -((int)(yre*10.0) - midh);
	  setPixel(x, y, color);
	  if (y-yprec > 1)
	    {
	      for(i = 0; y-yprec-i >= 1; i++)
		{
		  setPixel(x-1,yprec+i, color);
		}
	    }
	  if (y-yprec < -1)
	    {
	      for(i = 0; y-yprec+i <= -1; i++)
		{
		  setPixel(x-1,yprec-i,color);
		}
	    }
	  yprec = y;
	}
      if (yre > midh/10)
	{
	  if ((yprec > 0)&&(yprec < 640))
	    {
	      for(i = 0; yprec+i >= 0; i--)
		{
		  setPixel(x-1,yprec+i, color);
		}
	    }
	  yprec = 0;
	}
      if (yre < (midh/10) - 64)
	{
	  if ((yprec > 0)&&(yprec < 640))
	    {
	      for(i = 0; yprec+i+1 < 640; i++)
		{
		  setPixel(x-1,yprec+i, color);
		}
	    }
	  yprec = screen->h -1;
	}
      x++;
    }
}

llist add(llist liste, int valeur)
{
  element* nouvelElement = malloc(sizeof(element));
  nouvelElement->val = valeur;
  nouvelElement->nxt = liste;
  return nouvelElement;
}

float puissance(float x, int n)
{
float res = 1;
int i;
if (n < 0)
  {
    return 1/(puissance(x,-n));
  }

for (i = 0; i<n; i++)
  {
    res *= x;
  }
return res;
} 

float f(float x, llist liste)
{
  llist listebis = liste;
  if (listebis == NULL)
    {
      return 0;
    }
  else
    {
      float coef = (float)listebis->val;
      listebis = listebis->nxt;
      int deg = listebis->val;
      listebis = listebis->nxt;
      float yre = coef*puissance(x,deg);
      if (listebis != NULL)
	{
	  yre += f(x,listebis);
	}
      return yre;
    }
}

llist string_to_coef(char* s, llist liste)
{
  int i = 0;
  int coef = 0;
  int deg = 0;
  int neg = 0;
  while(s[i] != '\0')
    {
      if (s[i] == '-')
	{
	  neg = 1;
	  i++;
	}
      else if (s[i] == '+')
	{
	  i++;
	}
      if (s[i] == 'x')
	{
	  coef = 1;
	}
      else
	{
	  coef = (s[i] % 48);
	  i++;
	}
      if (s[i] != 'x')
	{
	  deg = 0;
	}
      else
	{
	  i++;
	  if(s[i] == '^')
	    {
	      i++;
	      deg = (s[i++] % 48);
	    }
	  else
	    {
	      deg = 1;
	    }
	}
      if (neg == 1)
	{
	  coef = - coef;
	  neg = 0;
	}
      liste = add(liste, deg);
      liste = add(liste, coef);
    }
  return liste;
}
void delspace(char* s)
{
  int src = 0;
  int dest = 0;
 
  while (s[src] != '\0')
    {
      if (s[src] != ' ')
	{
	  s[dest++] = s[src];
	}
      src++;
    } 
  s[dest] = '\0';
}

void loopy(void)
{
  SDL_Event event;
  int running = 1;
 
  while(running) 
    {
    while(SDL_PollEvent(&event)) 
      {
	switch(event.type)
	  {
	  case SDL_KEYDOWN:
	    switch(event.key.keysym.sym)
	      {
	      case SDLK_KP_PLUS:
		if(pctg <= 8)
		  {
		    pctg += 0.25;
		    redraw();
		  }
		break;
	      case SDLK_KP_MINUS:
		if(pctg >= 0.75)
		  {
		    pctg -= 0.25;
		    redraw();
		  }
		break;
	      case SDLK_LEFT:
		if(x <= 290)
		  {
		    x += 20;
		    redraw();
		  }
		break;
	      case SDLK_RIGHT:
		if(x >= -290)
		  {
		    x -= 20;
		    redraw();
		  }
		break;
	      case SDLK_DOWN:
		if(y >= -290)
		  {
		    y -= 20;
		    redraw();
		  }
		break;
	      case SDLK_UP:
		if(y <= 290)
		  {
		    y += 20;
		    redraw();
		  }
		break;
	      case SDLK_ESCAPE:
		running = 0;
	      }
	    break;
	  case SDL_MOUSEMOTION:
	    if(hold)
	      {
		onMouseMotion(event.motion);
		redraw();
	      }
	    break;
	  case SDL_MOUSEBUTTONUP:
	    hold = 0;
	    break;
	  case SDL_MOUSEBUTTONDOWN:
	    hold = 1;
	    break;

	  case SDL_QUIT:
	    running = 0;
	  }
      }
    }
}

void onMouseMotion(SDL_MouseMotionEvent mouse)
{
  x += mouse.xrel*0.3;
  y += mouse.yrel*0.3;
}
